<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    /* 
    如何判断函数中的this?
    1. 常规的情况: 看调用方法
        直接调用  fn()  => window/undefined
        new调用   new fn() => 新创建的实例
        对象调用   obj.fn()  => obj
        call/apply/bind()调用:  => 第一个参数对象
    2. 特殊情况
        箭头函数  => 沿着作用域链去外部查找this
    */



    // 自定义call
    Function.prototype.call = function (thisObj, ...args) {   // fn1.call.call(fn2)
      console.log('call')
      // return this(...args)

      // 处理thisObj的特殊值
      if (thisObj===null || thisObj===undefined) {
        // return this(...args)
        thisObj = window
      }

      // 给thisObj添加一个临时方法, 值为当前函数this
      thisObj.tempFn = this

      // 通过thisObj来调用方法 ==> 执行的就是当前函数   得到执行结果
      const result = thisObj.tempFn(...args)   // fn2.call()

      // 删除这个方法
      delete thisObj.tempFn

      // 返回执行结果
      return result
    }

    Function.prototype.apply = function (thisObj, args) { 
      return this.call(thisObj, ...args)
    }

    // bind: 绑定this和参数
    // 返回一个新函数, 新函数内部会调用原函数, 且this为bind指定的参数对象
    Function.prototype.bind = function (thisObj, ...args) {
      console.log('bind', thisObj, args)
      // 返回一个新函数
      return (...args2) => {
        // 新函数内部会调用原函数, 且this为bind指定的参数对象
        return this.call(thisObj, ...args, ...args2)
      }
    }

  </script>

  <script>
    function fn1() {
      console.log(1, this)
    }
    function fn2() {
      console.log(2, this)
    }

    // fn1.call(fn2)

    console.log(fn1.call)
    // call函数来调用call方法
    fn1.call.call(fn2)

  </script>

  <script>
    function fn(a, b) {
      console.log(a, b, this)
      return a + b
    }
    const obj = {m: 1}

    // 立即执行函数, 并指定内部的this为第一个参数
    // console.log(fn.call(obj, 2, 3))
    // console.log(fn.call(null, 2, 3))
    // console.log(fn.call(undefined, 2, 3))

    // bind: 绑定this和参数
    // 返回一个新函数, 新函数内部会调用原函数, 且this为bind指定的参数对象
    // fn.bind(obj)(4, 5)
    // fn.bind(obj, 6)(4, 5)
    // fn.bind(obj, 6, 7)(4, 5)
  </script>
</body>
</html>